Skip to content

fix(wallet): return error instead of panicking on invalid current_height#449

Open
none34829 wants to merge 1 commit intobitcoindevkit:masterfrom
none34829:fix/invalid-current-height
Open

fix(wallet): return error instead of panicking on invalid current_height#449
none34829 wants to merge 1 commit intobitcoindevkit:masterfrom
none34829:fix/invalid-current-height

Conversation

@none34829
Copy link
Copy Markdown
Contributor

Description

Closes #49.

TxBuilder::current_height and the fallback to chain.tip().height() in create_tx both called absolute::LockTime::from_height(h).expect(...), which panics whenever h >= 500_000_000. The Wizardsardine audit flagged this because it lets a remote Electrum/Esplora server crash the wallet by reporting a tip height at or above that threshold — panics on externally-provided input are unsafe for a library.

This PR replaces the panics with a typed error:

  • TxParams.current_height now stores a raw u32 instead of an already-validated absolute::LockTime, so the builder setter no longer has to validate in a position where it can't return a Result.
  • Wallet::create_tx performs the u32 -> LockTime conversion in one place, mapping the ConversionError to a new CreateTxError::InvalidCurrentHeight(u32) variant via ?.
  • Both the explicit-caller path (TxBuilder::current_height) and the implicit-fallback path (chain tip) now flow through the same validation and surface the same error.

Notes to the reviewers

  • The change to TxParams.current_height is crate-private (pub(crate)) so there's no external API break there. The TxBuilder::current_height public signature is unchanged.
  • Adding a new variant to CreateTxError is technically an additive change, but it is an enum so downstream exhaustive matches would need to be updated. CreateTxError is not marked #[non_exhaustive], which is tracked separately in Consider making all error enum variants #[non_exhaustive] #239.
  • Follows the audit recommendation: "it's safer to only panic on inconsistent internal state and not on externally provided inputs."

Changelog notice

  • Fixed: Wallet::create_tx now returns CreateTxError::InvalidCurrentHeight instead of panicking when TxBuilder::current_height or the fallback chain tip height is >= 500_000_000.

Checklists

All Submissions:

  • I've signed all my commits
  • I followed the contribution guidelines
  • I ran just p before pushing (140 lib tests + 118 wallet + 22 fee_bump + 10 persisted + 13 descriptor_macro + 54 doctests all pass; clippy clean; fmt clean; cargo doc clean)

Bugfixes:

  • This pull request breaks the existing API
  • I've added tests to reproduce the issue which are now passing
  • I'm linking the issue being fixed by this PR

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.21%. Comparing base (35a87f3) to head (6dac1a0).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #449   +/-   ##
=======================================
  Coverage   80.21%   80.21%           
=======================================
  Files          24       24           
  Lines        5348     5348           
  Branches      242      242           
=======================================
  Hits         4290     4290           
  Misses        980      980           
  Partials       78       78           
Flag Coverage Δ
rust 80.21% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread src/wallet/tx_builder.rs Outdated
@none34829 none34829 force-pushed the fix/invalid-current-height branch from 4065bc8 to b8a0b5e Compare April 23, 2026 10:26
@none34829
Copy link
Copy Markdown
Contributor Author

none34829 commented Apr 23, 2026

Thanks for the review- the type-safe approach is definitely cleaner.

  • TxBuilder::current_height now takes absolute::Height instead of u32, so the conversion to LockTime is infallible via impl From<Height> for LockTime. No expect, no new error variant.
  • The chain tip fallback in create_tx keeps the expect, but now with a message noting that an invalid LocalChain tip height is an upstream invariant violation rather than silently swallowing it.
  • CreateTxError::InvalidCurrentHeight removed entirely, along with the test that exercised it (the error case is gone).
  • Existing test call sites (test_create_tx_custom_locktime, test_spend_coinbase) updated to construct the height via absolute::Height::from_consensus(h).unwrap().

@ValuedMammal ValuedMammal added the api A breaking API change label Apr 23, 2026
@ValuedMammal ValuedMammal added this to the Wallet 4.0.0 milestone Apr 23, 2026
@ValuedMammal ValuedMammal moved this to In Progress in BDK Wallet Apr 23, 2026
@none34829
Copy link
Copy Markdown
Contributor Author

none34829 commented Apr 23, 2026

CI failures here are unrelated to this PR- bitcoin-units 0.1.3 was published and deprecated FeeRate::from_sat_per_vb_unchecked, which breaks clippy -D warnings on master too (47 errors locally when I reproduce against master).

I've opened #455 with a focused mechanical rename to from_sat_per_vb_u32. Once that lands I'll rebase this PR on top and CI should go green.

ValuedMammal added a commit that referenced this pull request Apr 23, 2026
…_unchecked

be710a8 chore(tests): replace deprecated FeeRate::from_sat_per_vb_unchecked (none34829)

Pull request description:

  ### Description

  `bitcoin-units 0.1.3` deprecated `FeeRate::from_sat_per_vb_unchecked` in favor of `FeeRate::from_sat_per_vb_u32`. Cargo resolves any 0.1.x automatically, so fresh CI runs started failing on master (and all open PRs) once 0.1.3 was published.

  This patch swaps every call site over to the new name. The two functions compute the same value for any input that fits in `u32`; every existing call site passes a small literal (1, 3, 5, 10, 25, 50, 255, 454, 1000, 10_000), so no behavioural change.

  All usages are in test/fixture code (`src/wallet/coin_selection.rs` test module, `tests/wallet.rs`, `tests/build_fee_bump.rs`) — no production code path was using the deprecated function.

  ### Notes to the reviewers

  - Pure mechanical rename; no logic or type changes.
  - Unblocks CI on master and every open PR (e.g. #449, #442, #445, #448).

  ### Changelog notice

  N/A — internal test-only change.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `just p` before pushing

ACKs for top commit:
  ValuedMammal:
    ACK be710a8

Tree-SHA512: b2354fcf6e39228bc5796f92af4d3692fbdb750267c2c5a7d814fc5c84f4af64b746562fda7e01dac282ba04acb17ff7bf5c7ef0155a3d6f302974003baf0629
@none34829 none34829 force-pushed the fix/invalid-current-height branch from b8a0b5e to 6dac1a0 Compare April 23, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api A breaking API change

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Transaction builder should handle invalid tip height

2 participants